package com.sinosoft.vaccinetoai.security.shiro;

import com.sinosoft.vaccinetoai.security.shiro.filter.CustomAuthenticationFilter;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.credential.CredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;

import javax.servlet.Filter;
import java.util.*;

/**
 * Shiro配置
 * @author xzh
 * @date 2023-04-08 14:49
 * @since 1.0.0
 */
@Configuration
public class ShiroConfig {

    //session 过期时间 1h
    private final long EXPIRE_SESSION_TIME = 60 * 60 * 1000L;

    /**
     * Shiro 过滤器
     * @param securityManager 安全管理器
     * @return {@link ShiroFilterFactoryBean }
     * @author xzh
     * @since 1.0.0
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        // 配置安全管理器
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        SecurityUtils.setSecurityManager(securityManager);

        /**
         * 自定义过滤器
         */
        Map<String, Filter> filters = new HashMap<>();
        filters.put("authc", new CustomAuthenticationFilter());
        shiroFilterFactoryBean.setFilters(filters);


        /**
         * 配置不会被拦截的链接，顺序判断
         */
        Map<String, String> filterChainDefinitionMap = new LinkedHashMap<>();

        filterChainDefinitionMap.put("/user/login", "anon");//用戶登陸
        filterChainDefinitionMap.put("/**", "authc, perms, roles");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);

        return shiroFilterFactoryBean;
    }

    /**
     * 多bean的处理规则
     * @return
     */
    @Bean
    public CustomModularRealmAuthenticator modularRealmAuthenticator(){
        return new CustomModularRealmAuthenticator();
    }

    /**
     * 创建自定义验证器
     * @return {@link CredentialsMatcher }
     * @author xzh
     * @since 1.0.0
     */
    @Bean
    public CredentialsMatcher credentialsMatcher() {
        return new CustomCredentialsMatcher();
    }


    @Bean
    public SSORealm ssoRealm(){
        SSORealm realm = new SSORealm();
        realm.setCredentialsMatcher(new CredentialsMatcher() {
            @Override
            public boolean doCredentialsMatch(AuthenticationToken authenticationToken, AuthenticationInfo authenticationInfo) {
                return true;
            }
        });
        return realm;
    }




    /**
     * 自定义会话管理器
     * @param sessionDAO Session数据访问对象
     * @return {@link SessionManager }
     * @author xzh
     * @since 1.0.0
     */
    @Bean
    public SessionManager sessionManager(@Autowired(required = false) SessionDAO sessionDAO) {
        CustomSessionManager sessionManager = new CustomSessionManager();
        sessionManager.setSessionIdCookieEnabled(false);
        sessionManager.setGlobalSessionTimeout(EXPIRE_SESSION_TIME);
        // 注入SessionDAO实例
        if(null != sessionDAO) {
            sessionManager.setSessionDAO(sessionDAO);
        }
        return sessionManager;
    }

    /**
     * 创建安全管理器
     * @param sessionManager 会话管理器
     * @param cacheManager 缓存管理器
     * @return {@link DefaultWebSecurityManager }
     * @author xzh
     * @since 1.0.0
     */
    @Bean
    public DefaultWebSecurityManager defaultWebSecurityManager(@Autowired(required = false) SessionManager sessionManager, @Autowired(required = false) CacheManager cacheManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

        List<Realm> realms = new ArrayList<>();

        //自己重写的ModularRealmAuthenticator
        CustomModularRealmAuthenticator modularRealmAuthenticator = new CustomModularRealmAuthenticator();
        modularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());

        //注入自定义多realm的处理其
        securityManager.setAuthenticator(modularRealmAuthenticator);

        //建立多匹配器
        realms.add(ssoRealm());
        securityManager.setRealms(realms);


        // 注入自定义缓存管理器
        securityManager.setCacheManager(cacheManager != null ? cacheManager : new MemoryConstrainedCacheManager());
        // 注入自定义会话管理器
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    /**
     * Shiro生命周期处理器
     */
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    /**
     * 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
     * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    @DependsOn({ "lifecycleBeanPostProcessor" })
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true); return advisorAutoProxyCreator;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager defaultWebSecurityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(defaultWebSecurityManager);
        return authorizationAttributeSourceAdvisor;
    }

}
